using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Reflection;
using Microsoft.Xna.Framework.Content;
using CreamX.Framework.Game;
using CreamX.Framework.Game.Components;

using CreamX.Framework;

using Microsoft.Xna.Framework;
using CreamX.Framework.SceneGraph;
using CreamX.Framework.Attributes;

namespace CreamXStudio.Classes
{
    public class SessionData
    {
        public List<Assembly> DefaultAssemblies = new List<Assembly>();
        public ContentManager contentManager;
        public CreamXProjectSettings ProjectSettings=null;
        public CreamXSceneSettings CurrentScene= null;
        public ContentBuilder ContentBuilder;
        public Dictionary<string,System.Drawing.Image> ImageCache;
        public static SessionData Instance;
        public SessionData()
        {
            ImageCache = new Dictionary<string, System.Drawing.Image>();
            ContentBuilder = new ContentBuilder();
        }
        static SessionData()
        {
            Instance = new SessionData();
        }

        public bool ProjectFileOpen=false;
        public bool SceneFileOpen = false;
        public void OpenProject(string filename)
        {
            ProjectSettings = CreamXProjectSettings.Load(filename);
            ProjectFileOpen = true;
        }
        public void OpenScene(string filename)
        {
            
            CurrentScene = CreamXSceneSettings.LoadSceneData(filename,ProjectSettings);
            SceneFileOpen= true;
        }

        internal string GetFullMaterialPath(string p)
        {
            string __name =Path.GetFullPath( ProjectSettings.RootDIR + p);
            return __name;
        }

        internal string GetNewMaterialName(string filename)
        {
            string s=Path.GetFileNameWithoutExtension(filename);
            while (true)
            {
                if (CurrentScene.SceneData.GetMaterial(s) == null)
                {
                    break;
                }
                else
                {
                    s += "1";
                }
            }
            return s;
        }

        internal void SaveProject()
        {
            ProjectSettings.Save();
            SaveData(CurrentScene.Filename);
        }
        string root;
        private void SaveData(string filename)
        {
            XmlDocument _xml = new XmlDocument();
            root = Path.GetDirectoryName(filename);
            
            CreamXSceneData _currentData = CurrentScene.SceneData;
            
            XmlNode Root = _xml.CreateElement("CreamXSceneData"); 
            XmlNode _node = _xml.AppendChild(Root);
            _node = Root.AppendChild(_xml.CreateElement("ContentRoot"));
            _node.Attributes.Append(_xml.CreateAttribute("value"));
            _node.Attributes["value"].InnerText = _currentData.ContentRoot;
            //Do Camera
            AddCameraData(_xml, _currentData, Root);
            //Do Materials
            AddMaterialsData(_xml, _currentData, Root);

            XmlNode _anims = Root.AppendChild(_xml.CreateElement("AnimationData"));
            AddAnimationData(_xml, _currentData, _anims);

            XmlNode _objects = Root.AppendChild(_xml.CreateElement("ObjectData"));
            foreach (CreamXObject _obj in _currentData.TemplateObjects)
            {
                XmlNode _mat = null;
                if (_obj is CreamXAnimatedObject)
                    _mat = _xml.CreateElement("AnimatedObject");
                else if (_obj is CreamXTileGrid)
                    _mat = _xml.CreateElement("CreamXTileGrid");
                else
                    _mat = _xml.CreateElement("Object");


                if (_obj.FileRef != string.Empty)
                {
                    _mat.Attributes.Append(_xml.CreateAttribute("fileref")).InnerText = _obj.FileRef;
                    AddObjectToFile(_obj,_mat);
                }
                else
                {
                    AddObject(_xml, _obj, _mat);
                }
                _objects.AppendChild(_mat);
            }
            foreach (CreamXObject _obj in _currentData.Objects)
            {
                XmlNode _mat = null;
                _obj.Template = false;
                if (_obj is CreamXAnimatedObject)
                    _mat = _xml.CreateElement("AnimatedObject");
                else if (_obj is CreamXTileGrid)
                    _mat = _xml.CreateElement("CreamXTileGrid");
                else
                    _mat = _xml.CreateElement("Object");

                if (_obj.FileRef != string.Empty && _obj.FileRef!=null)
                {
                    _mat.Attributes.Append(_xml.CreateAttribute("fileref")).InnerText = _obj.FileRef;
                    AddObjectToFile(_obj, _mat);
                }
                else
                {
                    AddObject(_xml, _obj, _mat);
                }
                _objects.AppendChild(_mat);
            }
            #region OLDCODE
            //foreach (CreamXObject _obj in _currentData.Objects)
            //{
            //    XmlNode _mat = null;
            //    if (_obj is CreamXAnimatedObject)
            //        _mat = _xml.CreateElement("AnimatedObject");
            //    else if (_obj is CreamXTileGrid)
            //        _mat = _xml.CreateElement("CreamXTileGrid");
            //    else
            //        _mat = _xml.CreateElement("Object");
            //    _mat.AppendChild(_xml.CreateElement("Name")).InnerText = _obj.Name;
            //    _mat.AppendChild(_xml.CreateElement("Scale")).InnerText = _obj.Scale.ToString();

            //    _node = _mat.AppendChild(_xml.CreateElement("Position"));
            //    _node.AppendChild(_xml.CreateElement("X")).InnerText = _obj.Position.X.ToString();
            //    _node.AppendChild(_xml.CreateElement("Y")).InnerText = _obj.Position.Y.ToString();
            //    _node = _mat.AppendChild(_xml.CreateElement("Origin"));
            //    _node.AppendChild(_xml.CreateElement("X")).InnerText = _obj.Origin.X.ToString();
            //    _node.AppendChild(_xml.CreateElement("Y")).InnerText = _obj.Origin.Y.ToString();

            //    _mat.AppendChild(_xml.CreateElement("FlipX")).InnerText = _obj.FlipX.ToString();
            //    _mat.AppendChild(_xml.CreateElement("FlipY")).InnerText = _obj.FlipY.ToString();
            //    _mat.AppendChild(_xml.CreateElement("Template")).InnerText = false.ToString();
            //    _mat.AppendChild(_xml.CreateElement("Layer")).InnerText = _obj.Layer.ToString();

            //    _mat.AppendChild(_xml.CreateElement("Rotation")).InnerText = _obj.Rotation.ToString();
            //    _mat.AppendChild(_xml.CreateElement("Visible")).InnerText = _obj.Visible.ToString();
            //    _mat.AppendChild(_xml.CreateElement("Color")).InnerText = _obj.Color.ToString();
            //    _mat.AppendChild(_xml.CreateElement("ObjectTypeAsVal")).InnerText = _obj.ObjectTypeFlags.ToString();
            //    if (_obj is CreamXAnimatedObject)
            //        _mat.AppendChild(_xml.CreateElement("AnimationInfo")).Attributes.Append(_xml.CreateAttribute("Ref")).InnerText = ((CreamXAnimatedObject)_obj).Animation.Name;
            //    _node = _mat.AppendChild(_xml.CreateElement("Material"));
            //    if (_obj.Material != null)
            //        _node.Attributes.Append(_xml.CreateAttribute("Ref")).InnerText = _obj.Material.Name;


            //    XmlNode __componentsNode = _mat.AppendChild(_xml.CreateElement("Components"));
            //    //Do Components
            //    foreach (CreamXComponent com in _obj.Components)
            //    {
            //        Type t = com.GetType();
            //        if (t.GetCustomAttributes(true).Length > 0)
            //        {
            //            _node = __componentsNode.AppendChild(_xml.CreateElement(t.FullName));
            //            //_node.Attributes.Append(_xml.CreateAttribute("Ref")).InnerText = _obj.Material.Name;
            //            _objects.AppendChild(_mat);

            //            foreach (PropertyInfo prop in t.GetProperties())
            //            {
            //                object[] attrib = prop.GetCustomAttributes(true);
            //                if (attrib.Length > 0)
            //                {
            //                    if (attrib[0] is CreamXComponentPropertyAttribute)
            //                    {
            //                        XmlNode __prop = _node.AppendChild(_xml.CreateElement(prop.Name));
            //                        if (prop.PropertyType.BaseType == typeof(Enum))
            //                        {
            //                            __prop.Attributes.Append(_xml.CreateAttribute("type")).InnerText = prop.PropertyType.FullName;
            //                            __prop.Attributes.Append(_xml.CreateAttribute("value")).InnerText = prop.GetValue(com, null).ToString();
            //                        }
            //                        else
            //                            __prop.Attributes.Append(_xml.CreateAttribute("value")).InnerText = prop.GetValue(com, null).ToString();

            //                    }
            //                }
            //            }
            //        }
            //    }
            //    _objects.AppendChild(_mat);
            //}
            /*foreach (CreamX.Framework.Game.GUI.GUIButton _obj in _currentData.GUI.Buttons)
            {
                XmlNode _mat = null;
                
                _mat = _xml.CreateElement("GUIButton");
                _mat.AppendChild(_xml.CreateElement("Name")).InnerText = _obj.Name;
                _mat.AppendChild(_xml.CreateElement("Scale")).InnerText = _obj.Scale.ToString();

                _node = _mat.AppendChild(_xml.CreateElement("Position"));
                _node.AppendChild(_xml.CreateElement("X")).InnerText = _obj.Position.X.ToString();
                _node.AppendChild(_xml.CreateElement("Y")).InnerText = _obj.Position.Y.ToString();
                _node = _mat.AppendChild(_xml.CreateElement("Origin"));
                _node.AppendChild(_xml.CreateElement("X")).InnerText = _obj.Origin.X.ToString();
                _node.AppendChild(_xml.CreateElement("Y")).InnerText = _obj.Origin.Y.ToString();

                _mat.AppendChild(_xml.CreateElement("FlipX")).InnerText = _obj.FlipX.ToString();
                _mat.AppendChild(_xml.CreateElement("FlipY")).InnerText = _obj.FlipY.ToString();
                _mat.AppendChild(_xml.CreateElement("Template")).InnerText = false.ToString();
                _mat.AppendChild(_xml.CreateElement("Layer")).InnerText = _obj.Layer.ToString();

                _mat.AppendChild(_xml.CreateElement("Rotation")).InnerText = _obj.Rotation.ToString();
                _mat.AppendChild(_xml.CreateElement("Visible")).InnerText = _obj.Visible.ToString();
                _mat.AppendChild(_xml.CreateElement("Color")).InnerText = _obj.Color.ToString();

                 _node = _mat.AppendChild(_xml.CreateElement("Material"));
                if (_obj.Material != null)
                    _node.Attributes.Append(_xml.CreateAttribute("Ref")).InnerText = _obj.Material.Name;


                XmlNode __componentsNode = _mat.AppendChild(_xml.CreateElement("Components"));
                //Do Components
                foreach (CreamXComponent com in _obj.Components)
                {
                    Type t = com.GetType();
                    if (t.GetCustomAttributes(true).Length > 0)
                    {
                        _node = __componentsNode.AppendChild(_xml.CreateElement(t.FullName));
                        //_node.Attributes.Append(_xml.CreateAttribute("Ref")).InnerText = _obj.Material.Name;
                        _objects.AppendChild(_mat);

                        foreach (PropertyInfo prop in t.GetProperties())
                        {
                            object[] attrib = prop.GetCustomAttributes(true);
                            if (attrib.Length > 0)
                            {
                                if (attrib[0] is CreamXComponentPropertyAttribute)
                                {
                                    XmlNode __prop = _node.AppendChild(_xml.CreateElement(prop.Name));
                                    if (prop.PropertyType.BaseType == typeof(Enum))
                                    {
                                        __prop.Attributes.Append(_xml.CreateAttribute("type")).InnerText = prop.PropertyType.FullName;
                                        __prop.Attributes.Append(_xml.CreateAttribute("value")).InnerText = prop.GetValue(com, null).ToString();
                                    }
                                    else
                                        __prop.Attributes.Append(_xml.CreateAttribute("value")).InnerText = prop.GetValue(com, null).ToString();

                                }
                            }
                        }
                    }
                }
                _objects.AppendChild(_mat);
            }
             * */
            #endregion
            _xml.Save(filename);
        }

        private static void AddMaterialsData(XmlDocument _xml, CreamXSceneData _currentData, XmlNode Root)
        {
            XmlNode _mats = Root.AppendChild(_xml.CreateElement("MaterialObjects"));
            foreach (CreamXMaterialBase __mat in _currentData.Materials)
            {
                XmlNode _mat = _xml.CreateElement("DefaultMaterial");
                _mat.Attributes.Append(_xml.CreateAttribute("name")).InnerText = __mat.Name;
                _mat.AppendChild(_xml.CreateElement("TextureFile")).InnerText = __mat.MaterialFilename;
                _mats.AppendChild(_mat);
            }
        }

        private static void AddCameraData(XmlDocument _xml, CreamXSceneData _currentData, XmlNode Root)
        {
            XmlNode _node = null;
            XmlNode _cam = Root.AppendChild(_xml.CreateElement("Camera2D"));

            _node = _cam.AppendChild(_xml.CreateElement("Position"));
            _node.AppendChild(_xml.CreateElement("X")).InnerText = _currentData.Camera.Position.X.ToString();
            _node.AppendChild(_xml.CreateElement("Y")).InnerText = _currentData.Camera.Position.Y.ToString();
            _node = _cam.AppendChild(_xml.CreateElement("Size"));
            _node.AppendChild(_xml.CreateElement("X")).InnerText = _currentData.Camera.Size.X.ToString();
            _node.AppendChild(_xml.CreateElement("Y")).InnerText = _currentData.Camera.Size.Y.ToString();
            _cam.AppendChild(_xml.CreateElement("Name")).InnerText = "Camera1";
        }

        private static XmlNode AddObject(XmlDocument xmlDoc, CreamXObject _obj, XmlNode _mat)
        {
            _mat.AppendChild(xmlDoc.CreateElement("Name")).InnerText = _obj.Name;
            _mat.AppendChild(xmlDoc.CreateElement("Scale")).InnerText = _obj.Scale.ToString();
            XmlNode _node;
            _node = _mat.AppendChild(xmlDoc.CreateElement("Position"));
            _node.AppendChild(xmlDoc.CreateElement("X")).InnerText = _obj.Position.X.ToString();
            _node.AppendChild(xmlDoc.CreateElement("Y")).InnerText = _obj.Position.Y.ToString();
            
            _node = _mat.AppendChild(xmlDoc.CreateElement("Origin"));
            _node.AppendChild(xmlDoc.CreateElement("X")).InnerText = _obj.Origin.X.ToString();
            _node.AppendChild(xmlDoc.CreateElement("Y")).InnerText = _obj.Origin.Y.ToString();

            _mat.AppendChild(xmlDoc.CreateElement("FlipX")).InnerText = _obj.FlipX.ToString();
            _mat.AppendChild(xmlDoc.CreateElement("FlipY")).InnerText = _obj.FlipY.ToString();
            _mat.AppendChild(xmlDoc.CreateElement("Template")).InnerText = _obj.Template.ToString();
            _mat.AppendChild(xmlDoc.CreateElement("Layer")).InnerText = _obj.Layer.ToString();

            _mat.AppendChild(xmlDoc.CreateElement("Rotation")).InnerText = _obj.Rotation.ToString();
            _mat.AppendChild(xmlDoc.CreateElement("Visible")).InnerText = _obj.Visible.ToString();
            _mat.AppendChild(xmlDoc.CreateElement("Color")).InnerText = _obj.Color.ToString();
            _mat.AppendChild(xmlDoc.CreateElement("ObjectTypeAsVal")).InnerText = _obj.ObjectTypeFlags.ToString();
            if (_obj is CreamXAnimatedObject)
                _mat.AppendChild(xmlDoc.CreateElement("AnimationInfo")).Attributes.Append(
                    xmlDoc.CreateAttribute("Ref")).InnerText = ((CreamXAnimatedObject)_obj).Animation.Name;
            _node = _mat.AppendChild(xmlDoc.CreateElement("Material"));
            if (_obj.Material != null)
                _node.Attributes.Append(xmlDoc.CreateAttribute("Ref")).InnerText = _obj.Material.Name;

            XmlNode __mounted = _mat.AppendChild(xmlDoc.CreateElement("MountedObjects"));
            foreach (Mount mnt in _obj.MountedObjects)
            {
                XmlNode __mnted = __mounted.AppendChild(xmlDoc.CreateElement("MountedObject"));
                __mnted.AppendChild(xmlDoc.CreateElement("Object")).Attributes.Append(xmlDoc.CreateAttribute("objectRef")).InnerText = mnt.ObjectRef;
                __mnted.AppendChild(xmlDoc.CreateElement("SnapToMount")).InnerText = mnt.SnapToMount.ToString();
                if(mnt.MountPoint!=null)
                    __mnted.AppendChild(xmlDoc.CreateElement("MountPoint")).InnerText = mnt.MountPoint.Name;
            
                __mnted.AppendChild(xmlDoc.CreateElement("InheritVisibility")).InnerText = mnt.InheritVisibility.ToString();
                __mnted.AppendChild(xmlDoc.CreateElement("SnapToMount")).InnerText = mnt.SnapToMount.ToString();
            
            }
            XmlNode __componentsNode = _mat.AppendChild(xmlDoc.CreateElement("Components"));
            //Do Components
            foreach (CreamXComponent com in _obj.Components)
            {
                Type t = com.GetType();
                if (t.GetCustomAttributes(true).Length > 0)
                {
                    _node = __componentsNode.AppendChild(xmlDoc.CreateElement(t.FullName));
                    //_node.Attributes.Append(_xml.CreateAttribute("Ref")).InnerText = _obj.Material.Name;
                   // _objects.AppendChild(_mat);

                    foreach (PropertyInfo prop in t.GetProperties())
                    {
                        object[] attrib = prop.GetCustomAttributes(true);
                        if (attrib.Length > 0)
                        {
                            if (attrib[0] is CreamXComponentPropertyAttribute)
                            {
                                XmlNode __prop = _node.AppendChild(xmlDoc.CreateElement(prop.Name));
                                if (prop.PropertyType.BaseType == typeof(Enum))
                                {
                                    __prop.Attributes.Append(xmlDoc.CreateAttribute("type")).InnerText = prop.PropertyType.FullName;
                                    __prop.Attributes.Append(xmlDoc.CreateAttribute("value")).InnerText = prop.GetValue(com, null).ToString();
                                }
                                else if (prop.PropertyType == typeof(List<TileLayer>))
                                {

                                    //__prop.Attributes.Append(_xml.CreateAttribute("type")).InnerText = prop.PropertyType.FullName;
                                    List<TileLayer> _pnts = (List<TileLayer>)prop.GetValue(com, null);
                                    foreach (TileLayer mntpnt in _pnts)
                                    {
                                        XmlNode __mntNode = __prop.AppendChild(xmlDoc.CreateElement("TileLayer"));
                                        __mntNode.Attributes.Append(xmlDoc.CreateAttribute("name")).InnerText = mntpnt.Name;
                                        __mntNode.Attributes.Append(xmlDoc.CreateAttribute("Order")).InnerText = mntpnt.Order.ToString();
                                        __mntNode.Attributes.Append(xmlDoc.CreateAttribute("Visible")).InnerText = mntpnt.Visible.ToString();
                                        __mntNode.InnerText = mntpnt.TileData;
                                    }

                                }
                                else if (prop.PropertyType == typeof(List<MountPoint>))
                                {

                                    //__prop.Attributes.Append(_xml.CreateAttribute("type")).InnerText = prop.PropertyType.FullName;
                                    List<MountPoint> _pnts = (List<MountPoint>)prop.GetValue(com, null);
                                    foreach (MountPoint mntpnt in _pnts)
                                    {
                                        XmlNode __mntNode = __prop.AppendChild(xmlDoc.CreateElement("MountPoint"));
                                        foreach (PropertyInfo prop1 in mntpnt.GetType().GetProperties())
                                        {
                                            if (prop1.PropertyType == typeof(Vector2))
                                            {
                                                XmlNode __vnode = __mntNode.AppendChild(xmlDoc.CreateElement(prop1.Name));
                                                Vector2 __v = (Vector2)prop1.GetValue(mntpnt, null);
                                                __vnode.AppendChild(xmlDoc.CreateElement("X")).InnerText = __v.X.ToString();
                                                __vnode.AppendChild(xmlDoc.CreateElement("Y")).InnerText = __v.Y.ToString();

                                            }
                                            else if (prop1.PropertyType == typeof(CreamXObject))
                                            {
                                                //string name = (mntpnt.Object == null) ? "" : mntpnt.Object.Name;
                                                //__mntNode.AppendChild(_xml.CreateElement(prop1.Name)).Attributes.Append(
                                                //    _xml.CreateAttribute("objectRef")).InnerText = name;
                                            }
                                            else
                                                __mntNode.AppendChild(xmlDoc.CreateElement(prop1.Name)).InnerText = prop1.GetValue(mntpnt, null).ToString();
                                        }
                                    }

                                }
                                else
                                    __prop.Attributes.Append(xmlDoc.CreateAttribute("value")).InnerText = prop.GetValue(com, null).ToString();

                            }
                        }
                    }
                }
            }
            return _node;
        }

        private static void AddAnimationData(XmlDocument _xml, CreamXSceneData _currentData, XmlNode _anims)
        {
            foreach (AnimationInfo _ani in _currentData.AnimationData)
            {
                XmlNode _mat = _xml.CreateElement("AnimationInfo");
                _mat.Attributes.Append(_xml.CreateAttribute("name")).InnerText = _ani.Name;
                _mat.AppendChild(_xml.CreateElement("Cols")).InnerText = _ani.TileCols.ToString();
                _mat.AppendChild(_xml.CreateElement("Rows")).InnerText = _ani.TileRows.ToString();
                _mat.AppendChild(_xml.CreateElement("SizeX")).InnerText = _ani.SizeX.ToString();
                _mat.AppendChild(_xml.CreateElement("SizeY")).InnerText = _ani.SizeY.ToString();
                _mat.AppendChild(_xml.CreateElement("FrameCount")).InnerText = _ani.FrameCount.ToString();
                _mat.AppendChild(_xml.CreateElement("AnimationSpeed")).InnerText = _ani.AnimationSpeed.ToString();
                _mat.AppendChild(_xml.CreateElement("CurrentFrame")).InnerText = _ani.CurrentFrame.ToString();
                _mat.AppendChild(_xml.CreateElement("StartFrame")).InnerText = _ani.StartFrame.ToString();
                _mat.AppendChild(_xml.CreateElement("EndFrame")).InnerText = _ani.EndFrame.ToString();
                _anims.AppendChild(_mat);
            }
        }

        private void AddObjectToFile(CreamXObject _obj,XmlNode node)
        {
            XmlDocument doc = new XmlDocument();

            XmlNode no = doc.AppendChild(doc.CreateElement(node.Name));

             AddObject(doc, _obj, no);

            doc.Save(root + "\\" + _obj.FileRef);
        }

        public void LoadAssemblies(string rootpath)
        {

            string __rootPath = rootpath;
            foreach (string __fil in Directory.GetFiles(__rootPath))
            {
                try
                {
                    string ext = Path.GetExtension(__fil);
                    if (Path.GetExtension(__fil) != ".exe" && Path.GetExtension(__fil) != ".dll")
                        continue;
                    Assembly __asm = Assembly.LoadFile(__fil);
                    if (__asm != null && HasCustomComponent(__asm))
                    {
                        DefaultAssemblies.Add(__asm);
                    }
                }
                catch (Exception err)
                {
                    CreamXErrorHandler.LogError(err);
                }
            }

        }

        private bool HasCustomComponent(Assembly __asm)
        {
            foreach (Type __type in __asm.GetTypes())
            {
                if (__type.Name.Contains("Component"))
                    Console.Write("");
                //object[] __attribs = __type.GetCustomAttributes(typeof(CreamX.Framework.Game.Attributes.CreamXComponentAttribute), true);
                object[] __attribs = __type.GetCustomAttributes(true);
                if (__attribs.Length > 0)
                {
                    foreach (object var in __attribs)
                    {
                        if (var.GetType().Name == typeof(CreamXComponentAttribute).Name)
                            return true;
                    }

                }

            }
            return false;
        }
    }
}
